package com.devgroupvn.dota.youtube;

/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.Formatter;
import java.util.Locale;

import android.content.Context;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

import com.devgroupvn.dota.R;

/**
 * A view containing controls for a MediaPlayer. Typically contains the buttons
 * like "Play/Pause", "Rewind", "Fast Forward" and a progress slider. It takes
 * care of synchronizing the controls with the state of the MediaPlayer.
 * <p>
 * The way to use this class is to instantiate it programatically. The
 * MediaController will create a default set of controls and put them in a
 * window floating above your application. Specifically, the controls will float
 * above the view specified with setAnchorView(). The window will disappear if
 * left idle for three seconds and reappear when the user touches the anchor
 * view.
 * <p>
 * Functions like show() and hide() have no effect when MediaController is
 * created in an xml layout.
 * 
 * MediaController will hide and show the buttons according to these rules:
 * <ul>
 * <li>The "previous" and "next" buttons are hidden until setPrevNextListeners()
 * has been called
 * <li>The "previous" and "next" buttons are visible but disabled if
 * setPrevNextListeners() was called with null listeners
 * <li>The "rewind" and "fastforward" buttons are shown unless requested
 * otherwise by using the MediaController(Context, boolean) constructor with the
 * boolean set to false
 * </ul>
 */
public class MyMediaController extends FrameLayout
{

	private MediaPlayerControl mPlayer;
	private View mAnchor;
	private View mRoot;
	private Window mWindow;
	private View mDecor;
	private SeekBar mProgress;
	private ProgressBar progressBarDownloading;
	private TextView percentDownloading;
	private ImageView imageComplete;
	private TextView mEndTime, mCurrentTime;
	private boolean mShowing;
	private boolean mDragging;
	private static final int sDefaultTimeout = 3000;
	private static final int FADE_OUT = 1;
	private static final int SHOW_PROGRESS = 2;
	private View.OnClickListener mFullScreenListener;
	StringBuilder mFormatBuilder;
	Formatter mFormatter;
	private Button mPauseButton;
	private Button mFullScreen;

	public MyMediaController(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		mRoot = this;
	}

	@Override
	public void onFinishInflate()
	{
		if (mRoot != null)
			initControllerView(mRoot);
	}

	public MyMediaController(Context context, boolean useFastForward)
	{
		super(context);
		initFloatingWindow();
	}

	public MyMediaController(Context context)
	{
		super(context);
		initFloatingWindow();
	}

	public MyMediaController(Context context, Window window, WindowManager manager, View decorView, View controller)
	{
		super(context);
		mWindow = window;
		mDecor = decorView;
		mRoot = controller;
		makeControllerView();
		initFloatingWindow();
	}

	private void initFloatingWindow()
	{

		mDecor.setOnTouchListener(mTouchListener);
		// While the media controller is up, the volume control keys should
		// affect the media stream type
		mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);

		setFocusable(true);
		setFocusableInTouchMode(true);
		setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
		requestFocus();
	}

	private OnTouchListener mTouchListener = new OnTouchListener()
	{
		public boolean onTouch(View v, MotionEvent event)
		{
			if (event.getAction() == MotionEvent.ACTION_DOWN)
			{
				if (mShowing)
				{
					// hide();
				}
			}
			return false;
		}
	};

	public void setMediaPlayer(MediaPlayerControl player)
	{
		mPlayer = player;
		updatePausePlay();
	}

	/**
	 * Set the view that acts as the anchor for the control view. This can for
	 * example be a VideoView, or your Activity's main view.
	 * 
	 * @param view
	 *            The view to which to anchor the controller when it is visible.
	 */
	public void setAnchorView(View view)
	{
		mAnchor = view;

		// FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
		// ViewGroup.LayoutParams.FILL_PARENT,
		// ViewGroup.LayoutParams.FILL_PARENT
		// );
		//
		// removeAllViews();
		// View v = makeControllerView();
		// addView(v, frameParams);
	}

	/**
	 * Create the view that holds the widgets that control playback. Derived
	 * classes can override this to create their own.
	 * 
	 * @return The controller view.
	 * @hide This doesn't work as advertised
	 */
	protected View makeControllerView()
	{
		initControllerView(mRoot);
		return mRoot;
	}

	private void initControllerView(View v)
	{
		mPauseButton = (Button) v.findViewById(R.id.PauseVideoButton);
		if (mPauseButton != null)
		{
			mPauseButton.requestFocus();
			mPauseButton.setOnClickListener(mPauseListener);
		}

		mProgress = (SeekBar) v.findViewById(R.id.VideoSeekBar);
		if (mProgress != null)
		{
			mProgress.setOnSeekBarChangeListener(mSeekListener);
			mProgress.setMax(1000);
		}

		progressBarDownloading = (ProgressBar) v.findViewById(R.id.progressBarDownloading);
		progressBarDownloading.setVisibility(ProgressBar.INVISIBLE);
		percentDownloading = (TextView) v.findViewById(R.id.percentDownloading);
		imageComplete = (ImageView) v.findViewById(R.id.imageComplete);
		mFullScreen = (Button) v.findViewById(R.id.FullScreenToggleButton);

		mEndTime = (TextView) v.findViewById(R.id.time);
		mCurrentTime = (TextView) v.findViewById(R.id.time_current);
		mFormatBuilder = new StringBuilder();
		mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());

		installFullScreenListeners();
	}

	/**
	 * Show the controller on screen. It will go away automatically after 3
	 * seconds of inactivity.
	 */
	public void show()
	{
		show(sDefaultTimeout);
	}

	/**
	 * Show the controller on screen. It will go away automatically after
	 * 'timeout' milliseconds of inactivity.
	 * 
	 * @param timeout
	 *            The timeout in milliseconds. Use 0 to show the controller
	 *            until hide() is called.
	 */
	public void show(int timeout)
	{

		if (!mShowing && mAnchor != null)
		{
			setProgress();
			mRoot.setVisibility(View.VISIBLE);
			mShowing = true;
		}
		updatePausePlay();

		// cause the progress bar to be updated even if mShowing
		// was already true. This happens, for example, if we're
		// paused with the progress bar showing the user hits play.
		mHandler.sendEmptyMessage(SHOW_PROGRESS);

		Message msg = mHandler.obtainMessage(FADE_OUT);
		if (timeout != 0)
		{
			mHandler.removeMessages(FADE_OUT);
			mHandler.sendMessageDelayed(msg, timeout);
		}
	}

	public boolean isShowing()
	{
		return mShowing;
	}

	/**
	 * Remove the controller from the screen.
	 */
	public void hide()
	{
		if (mAnchor == null)
			return;

		if (mShowing)
		{
			try
			{
				mHandler.removeMessages(SHOW_PROGRESS);
				mRoot.setVisibility(View.GONE);
			}
			catch (IllegalArgumentException ex)
			{
				Log.w("MediaController", "already removed");
			}
			mShowing = false;
		}
	}

	private Handler mHandler = new Handler()
	{
		@Override
		public void handleMessage(Message msg)
		{
			int pos;
			switch (msg.what)
			{
			case FADE_OUT:
				hide();
				break;
			case SHOW_PROGRESS:
				pos = setProgress();
				if (!mDragging && mShowing && mPlayer.isPlaying())
				{
					msg = obtainMessage(SHOW_PROGRESS);
					sendMessageDelayed(msg, 1000 - (pos % 1000));
				}
				break;
			}
		}
	};

	private String stringForTime(int timeMs)
	{
		int totalSeconds = timeMs / 1000;

		int seconds = totalSeconds % 60;
		int minutes = (totalSeconds / 60) % 60;
		int hours = totalSeconds / 3600;

		mFormatBuilder.setLength(0);
		if (hours > 0)
		{
			return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
		}
		else
		{
			return mFormatter.format("%02d:%02d", minutes, seconds).toString();
		}
	}

	private int setProgress()
	{
		if (mPlayer == null || mDragging) { return 0; }
		int position = mPlayer.getCurrentPosition();
		int duration = mPlayer.getDuration();
		if (mProgress != null)
		{
			if (duration > 0)
			{
				// use long to avoid overflow
				long pos = 1000L * position / duration;
				mProgress.setProgress((int) pos);
			}
			int percent = mPlayer.getBufferPercentage();
			mProgress.setSecondaryProgress(percent * 10);
			percentDownloading.setText(percent + "%");
			if (percent == 100)
			{
				progressBarDownloading.setVisibility(ProgressBar.INVISIBLE);
				imageComplete.setVisibility(ImageView.VISIBLE);
				percentDownloading.setVisibility(TextView.INVISIBLE);
			}
			else if (progressBarDownloading.getVisibility() == ProgressBar.INVISIBLE)
			{
				progressBarDownloading.setVisibility(ProgressBar.VISIBLE);
				imageComplete.setVisibility(ImageView.INVISIBLE);
				percentDownloading.setVisibility(TextView.VISIBLE);
			}
		}

		if (mEndTime != null)
			mEndTime.setText(stringForTime(duration));
		if (mCurrentTime != null)
			mCurrentTime.setText(stringForTime(position));

		return position;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event)
	{
		show(sDefaultTimeout);
		return true;
	}

	@Override
	public boolean onTrackballEvent(MotionEvent ev)
	{
		show(sDefaultTimeout);
		return false;
	}

	@Override
	public boolean dispatchKeyEvent(KeyEvent event)
	{
		int keyCode = event.getKeyCode();
		if (event.getRepeatCount() == 0
				&& (event.getAction() == KeyEvent.ACTION_DOWN)
				&& (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE))
		{
			doPauseResume();
			show(sDefaultTimeout);
			return true;
		}
		else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP)
		{
			if (mPlayer.isPlaying())
			{
				mPlayer.pause();
				updatePausePlay();
			}
			return true;
		}
		else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)
		{
			// don't show the controls for volume adjustment
			return super.dispatchKeyEvent(event);
		}
		else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)
		{
			hide();

			return true;
		}
		else
		{
			show(sDefaultTimeout);
		}
		return super.dispatchKeyEvent(event);
	}

	private View.OnClickListener mPauseListener = new View.OnClickListener()
	{
		public void onClick(View v)
		{
			doPauseResume();
			show(sDefaultTimeout);
		}
	};

	private void updatePausePlay()
	{
		if (mPlayer == null)
			return;
		if (mRoot == null)
			return;

		Button button = (Button) mRoot.findViewById(R.id.PauseVideoButton);
		if (button == null)
			return;

		if (mPlayer.isPlaying())
		{
			button.setBackgroundResource(R.drawable.pause);
		}
		else
		{
			button.setBackgroundResource(R.drawable.play);
		}
	}

	private void doPauseResume()
	{
		if (mPlayer == null)
			return;
		if (mPlayer.isPlaying())
		{
			mPlayer.pause();
		}
		else
		{
			mPlayer.start();
		}
		updatePausePlay();
	}

	private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener()
	{
		long duration;

		public void onStartTrackingTouch(SeekBar bar)
		{
			if (mPlayer == null)
				return;
			show(3600000);
			duration = mPlayer.getDuration();
		}

		public void onProgressChanged(SeekBar bar, int progress, boolean fromtouch)
		{
			if (mPlayer == null)
				return;
			if (fromtouch)
			{
				mDragging = true;
				duration = mPlayer.getDuration();
				long newposition = (duration * progress) / 1000L;
				mPlayer.seekTo((int) newposition);
				if (mCurrentTime != null)
					mCurrentTime.setText(stringForTime((int) newposition));
			}
		}

		public void onStopTrackingTouch(SeekBar bar)
		{
			mDragging = false;
			setProgress();
			updatePausePlay();
			show(sDefaultTimeout);
		}
	};

	@Override
	public void setEnabled(boolean enabled)
	{
		if (mPauseButton != null)
		{
			mPauseButton.setEnabled(enabled);
		}

		if (mProgress != null)
		{
			mProgress.setEnabled(enabled);
		}

		if (mFullScreen != null)
		{
			mFullScreen.setEnabled(enabled);
		}

		super.setEnabled(enabled);
	}

	private void installFullScreenListeners()
	{
		if (mFullScreen != null)
		{
			mFullScreen.setOnClickListener(mFullScreenListener);
			mFullScreen.setEnabled(mFullScreen != null);
		}
	}

	public void setFullscreenListeners(View.OnClickListener fullscreen)
	{
		mFullScreenListener = fullscreen;
		if (mRoot != null)
		{
			installFullScreenListeners();

			if (mFullScreen != null)
			{
				mFullScreen.setVisibility(View.VISIBLE);
			}
		}
	}

	public interface MediaPlayerControl
	{
		void start();

		void pause();

		int getDuration();

		int getCurrentPosition();

		void seekTo(int pos);

		boolean isPlaying();

		int getBufferPercentage();
	};
}
